﻿using Castle.DynamicProxy;
using CmsCore;
using CmsCore.Aop;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace AopTest2.Middleware
{
    public class AopCacheIntercept : Castle.DynamicProxy.IInterceptor
    {

        static Dictionary<string,string> mDic=new Dictionary<string, string>();

        public void Intercept(IInvocation invocation)
        {
            try
            {

                Type returnType = invocation.Method.ReturnType;

                //无返回值的 不用缓存
                if (returnType == typeof(void))
                {
                    invocation.Proceed();
                    return;
                }
               
                MethodInfo method=invocation.MethodInvocationTarget??invocation.Method;

                var cacheAttObj = method.GetCustomAttributes(typeof(CacheableAttribute), false).FirstOrDefault();

                if(cacheAttObj is CacheableAttribute cacheAttr)
                {
                    string cacheKey = GenerateCacheKey(cacheAttr.CacheKey, invocation);

                    if (mDic.ContainsKey(cacheKey))
                    {
                        string cacheValue=mDic[cacheKey];

                        //将当前获取到的缓存值，赋值给当前执行方法
                        Type[] resultTypes = returnType.GenericTypeArguments;

                        if (returnType == typeof(Task))
                        {
                            invocation.ReturnValue = InterceptAsync(cacheKey, (Task)invocation.ReturnValue);
                        }
                        else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
                        {
                            dynamic d = JsonConvert.DeserializeObject(cacheValue, resultTypes.FirstOrDefault());
                            invocation.ReturnValue = Task.FromResult(d);
                        }
                        else
                        {
                            invocation.ReturnValue = JsonConvert.DeserializeObject(cacheValue, returnType);
                        }

                        return;


                    }



                    invocation.Proceed();

                    if (returnType == typeof(Task))
                    {
                        invocation.ReturnValue = InterceptAsync(cacheKey, (Task)invocation.ReturnValue);
                    }
                    else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
                    {
                        invocation.ReturnValue = InterceptAsync(cacheKey, (dynamic)invocation.ReturnValue);
                    }
                    else
                    {
                       // int ExpireSeconds = int.Parse(_configuration["Cache:ExpireSeconds"].ToString());

                        mDic.Add(cacheKey, JsonConvert.SerializeObject(invocation.ReturnValue));
                    }
                    return;


                }




                invocation.Proceed();





            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw ;
            }

        }

        /// <summary>
        /// 获取自定义缓存键
        /// </summary>
        /// <param name="cacheKey"></param>
        /// <param name="invocation"></param>
        /// <returns></returns>
        private string GenerateCacheKey(string cacheKey, IInvocation invocation)
        {

            string className = invocation.TargetType.Name;
            string methodName = invocation.Method.Name;
            List<object> methodArguments = invocation.Arguments.ToList();
            string param = string.Empty;
            if (methodArguments.Count > 0)
            {
                string serializeString = JsonConvert.SerializeObject(methodArguments, Formatting.Indented, new JsonSerializerSettings
                {
                    DefaultValueHandling = DefaultValueHandling.Ignore
                });
                param = ":" + EncryptUtil.Encrypt(serializeString);
            }
            return string.Concat(cacheKey ?? $"{className}:{methodName}", param);

        }



        // do the continuation work for Task...
        private async Task InterceptAsync(string cacheKey, Task task)
        {
            await task.ConfigureAwait(false);
        }


        /// <summary>
        /// 异步缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="cacheKey"></param>
        /// <param name="task"></param>
        /// <returns></returns>
        private async Task<T> InterceptAsync<T>(string cacheKey, Task<T> task)
        {
            T result = await task.ConfigureAwait(false);
            //int ExpireSeconds = int.Parse(_configuration["Cache:ExpireSeconds"].ToString());
            // await RedisHelper.SetAsync(cacheKey, JsonConvert.SerializeObject(result), ExpireSeconds);

            mDic.Add(cacheKey, JsonConvert.SerializeObject(result));

            return result;
        }

    }
}
